package com.zyt.wiki.filter;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StreamUtils;

/**
 *   自定义实现HttpRequest的包装器
 *   （将request中的流复制保存，以便拦截器中读取流数据后，控制器中还能正常获取到request数据，不会丢失）
 * @author ytzhu
 *
 */
public class MyHttpRequestWrapper extends HttpServletRequestWrapper{

    private Log log = LogFactory.getLog(this.getClass()) ;
    private byte[] requestBody = null; //用于将流保存下来

    /**
     *  构造函数中，拷贝一份request的数据流
     *  （拷贝流的方法，会从request中读取一次数据，此后request中就无法再读取了，要通过当前类的备份流读取）
     * @param request
     */
    public MyHttpRequestWrapper(HttpServletRequest request) {
        super(request);
        try {
            this.requestBody = StreamUtils.copyToByteArray(request.getInputStream()) ;
        } catch (IOException e) {
            this.log.error("request inputStream copy error !") ;
        }
    }

    /**
     *  重写获取输入流方法，将流对象的数据读取改为从备份流中读取（父类中原逻辑是直接request.getInputStream()）
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if(this.requestBody == null) {
            this.requestBody = new byte[0] ;
        }

        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.requestBody) ;

        // 直接返回一个内部匿名类，读取数据的方法实现：从备份流中读取
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read() ;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }
        } ;

    }

    /**
     *  重写getReader()方法，在上面重写的输入流基础上，封装出BufferedReader，方便调用方按行读取数据（父类中原逻辑是直接request.getReader()）
     */
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

}
